package com.xb.loan.encryptor.classloader;


import com.xb.loan.encryptor.bean.Head;
import com.xb.loan.encryptor.bean.request.QueryClassReq;
import com.xb.loan.encryptor.bean.response.QueryClassRep;
import com.xb.loan.util.DateHelper;
import com.xb.loan.util.binaryConvert.BinaryParseUtil;
import com.xb.loan.util.binaryConvert.ConvertUtil;
import com.xb.loan.util.binaryConvert.CryptUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.*;

public class CryptographClassLoader extends ClassLoader {
	
	private static Log log = LogFactory.getLog(CryptographClassLoader.class);
	
	private Map<String, Class<?>> loadedClassMap = new HashMap<String, Class<?>>();
	
	public CryptographClassLoader(ClassLoader parent){
		
		super(parent);
	}
	
	@Override
	protected synchronized Class<?> loadClass(String name, boolean resolve)
			throws ClassNotFoundException {
		
		log.info("CryptographClassLoader myClass loader.." + name);
		
		Class<?> superFind = null;
		
		try {
			
			superFind = super.loadClass(name, resolve);
		} catch (Exception e) {

//			e.printStackTrace();
		}
		
		if(superFind != null){
			
			return superFind;
		}
		
		// First, check if the class has already been loaded
		Class<?> c = findloadedClass(name);
		
		if(c == null){
			
			c = myFindClass(name);
			
			loadedClassMap.put(name, c);
		}
		
		return c;
	}
	
	private Class<?> findloadedClass(String name) {
		
		return loadedClassMap.get(name);
	}

	private Class<?> myFindClass(String name){
		
		byte[] data = getClassData(name);
		
		if(data == null){
			
			return null;
		}
		
		return defineClass(name, data, 0, data.length);
	}
	
	private static byte[] getClassData(String name){
		
		ClassLoaderConfig config = ClassLoaderConfig.getConfig("crypt");
		
		byte[] data = null;
		
		Socket socket = null;
		
		try {
			
			socket = new Socket(config.getServerIp(), Integer.parseInt(config.getPort()));
			
			socket.setSoTimeout(Integer.parseInt(config.getSotimeout()));
			
			OutputStream out = socket.getOutputStream();
						
			out.write(creatSendData(name));
			
			DataInputStream in = new DataInputStream(socket.getInputStream());
			
			int len = in.readShort();
			
			byte[] remain = new byte[len];
			
			in.readFully(remain);
			
			byte[] body = Arrays.copyOfRange(remain, 6,remain.length);
			
			byte[] resBits = new byte[body.length - 1];
			
			int result = CryptUtil.decode(body, resBits, body.length);
			// 解密不通过
			if(result <=0){
				
				return null;
			}
			
			QueryClassRep rep = BinaryParseUtil.convert(resBits, QueryClassRep.class);

			List<Byte> classData = rep.getClassData();
			
			if(classData == null){
				
				return null;
			}
			
			data = new byte[classData.size()];
			
			for (int i = 0; i < classData.size(); i++) {
				
				data[i] = classData.get(i);
			}
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally{
			
			if(socket != null){
				
				try {
					socket.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		
		return data;
	}
	
	private static byte[] creatSendData(String className){
		
		QueryClassReq req = new QueryClassReq();
		
		Head head = new Head();
		
		head.setGuid(ClassLoaderConfig.getConfig("crypt").getGuid());
		head.setTimeStamp(DateHelper.formatDate("yyyyMMddHHmmss", new Date()));
		head.setTranscode("102");
		
		req.setHead(head);
		req.setClassName(className);
		
		byte[] info = BinaryParseUtil.format(req);
		
		byte[] body = new byte[info.length + 1];
		
		CryptUtil.encode(info, body, info.length);
		// 版本号（大，小，transcode）
		byte[] base = new byte[]{1, 0, 3, 49, 48, 50};
		
		byte[] sendData = new byte[base.length + 2 + body.length];
		// 加入包头
		ConvertUtil.putShort(sendData, 0, (short) (sendData.length - 2));
		
		ConvertUtil.put(sendData, 2, base);
		
		ConvertUtil.put(sendData, 8, body);
		
		return sendData;
	}
	
	public static void main(String[] args) {
		
		//getClassData("com.palm.commerce.convert.core.parser.bool.BoolParser");
	}
	
}
